package main;

import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Color;

import java.awt.Graphics;

import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.net.URL;

import javax.swing.plaf.SliderUI;

/**
 * Ball Class extends Applet.
 */
public class Ball extends Applet implements ICollider {
    /** Bounding box for the ball. */
    private Ellipse2D ballEllipse2d;
    /** Ball speed. */
    private Point2D speed;
    /** Sound. */
    private AudioClip bounce;
    private AudioClip explosion;
    /** File location. */
    private URL bounceUrl;
    private URL explosionUrl;
    /** Last position before collide. */
    private Point2D beforeCollide;

    /**
     * Default Ball Constructor.
     *
     * @param x
     *            upper left corner x
     * @param y
     *            upper left corner y
     * @param w
     *            width
     * @param h
     *            height
     */
    public Ball(final double x, final double y,
            final double w, final double h) {
        ballEllipse2d = new Ellipse2D.Double();
        ballEllipse2d.setFrame(x, y, w, h);
        speed = new Point2D.Double(0, 0);
        bounceUrl = this.getClass().getResource("../sound/bounce.wav");
        bounce = Applet.newAudioClip(bounceUrl);
        explosionUrl = this.getClass().getResource("../sound/explosion.wav");
        explosion = Applet.newAudioClip(explosionUrl);
        beforeCollide = new Point2D.Double(0, 0);
    }

    @Override
    public final Point2D calculateNextPosition(final float timeDelta) {
        double x = getPosition().getX() + getSpeed().getX() * timeDelta;
        double y = getPosition().getY() + getSpeed().getY() * timeDelta;
        Point2D newPos = new Point2D.Double(x, y);
        return newPos;
    }

    @Override
    public final Point2D getMax() {
        Point2D max = new Point2D.Double(ballEllipse2d.getMaxX(), ballEllipse2d
                .getMaxY());
        return max;
    }

    @Override
    public final Point2D getMin() {
        Point2D min = new Point2D.Double(ballEllipse2d.getMinX(), ballEllipse2d
                .getMinY());
        return min;
    }

    @Override
    public final Point2D getPosition() {
        return new Point2D.Double(ballEllipse2d.getCenterX(), ballEllipse2d
                .getCenterY());
    }

    @Override
    public final Point2D getSpeed() {
        return speed;
    }

    @Override
    public final boolean onHit(final ICollider hitBy) {
        restoreLastPosition();
        Point2D myMin = getMin();
        Point2D myMax = getMax();
        Point2D hitMax = hitBy.getMax();
        Point2D hitMin = hitBy.getMin();
        Point2D crtSpeedPoint2d = speed;
        if (isInLeftOrRightSide(myMin, myMax, hitMax, hitMin)) {
            reverseXspeed(crtSpeedPoint2d);
        } else if (isInUpOrDownSide(myMin, myMax, hitMax, hitMin)) {
            reverseYspeed(crtSpeedPoint2d);
        }
        if(hitBy instanceof Brick){
            explosion.play();
        }else{
            bounce.play();
        }
        return true;
    }
    /**
     * Test ball on left or right side of paddle.
     * @param myMin ball min
     * @param myMax ball max
     * @param hitMax hit object max
     * @param hitMin hit object min
     * @return true if on left or right
     */
    private boolean isInLeftOrRightSide(
            final Point2D myMin, final Point2D myMax,
            final Point2D hitMax, final Point2D hitMin) {
        return (myMax.getX() <= hitMin.getX() || myMin.getX() >= hitMax.getX());
    }
    /**
     * Test ball on top or button side of paddle.
     * @param myMin ball min
     * @param myMax ball max
     * @param hitMax hit object max
     * @param hitMin hit object min
     * @return according to test result
     */
    private boolean isInUpOrDownSide(final Point2D myMin, final Point2D myMax,
            final Point2D hitMax, final Point2D hitMin) {
        return (myMax.getY() <= hitMin.getY() || myMin.getY() >= hitMax.getY());
    }
    /**
     * Reverse ball speed in x.
     * @param crtSpeedPoint2d current speed.
     */
    private void reverseXspeed(final Point2D crtSpeedPoint2d) {
        speed.setLocation(-crtSpeedPoint2d.getX(), crtSpeedPoint2d.getY());
    }
    /**
     * Reverse ball speed in y.
     * @param crtSpeedPoint2d current speed.
     */
    private void reverseYspeed(final Point2D crtSpeedPoint2d) {
        speed.setLocation(crtSpeedPoint2d.getX(), -crtSpeedPoint2d.getY());
    }

    @Override
    public final void setPosition(final Point2D pos) {
        double width = ballEllipse2d.getWidth();
        double height = ballEllipse2d.getHeight();
        double newX = pos.getX() - width / 2;
        double newY = pos.getY() - height / 2;
        ballEllipse2d.setFrame(newX, newY, width, height);
    }

    @Override
    public final void setSpeed(final Point2D newSpeed) {
        this.speed = newSpeed;
    }
    @Override
    public final void paint(final Graphics g) {
        Color c = g.getColor();
        g.setColor(Color.red);
        g
                .fillOval((int) ballEllipse2d.getX(), (int) ballEllipse2d
                        .getY(), (int) ballEllipse2d.getWidth(),
                        (int) ballEllipse2d.getHeight());
        g.setColor(c);
    }

    @Override
    public final void saveLastPosition() {
        beforeCollide = getPosition();
    }

    @Override
    public final void restoreLastPosition() {
        setPosition(beforeCollide);
    }

}
